//! log 和 tracing 集成示例
//!
//! 本示例演示了如何在 Rust 中集成使用 log 和 tracing 两个日志库
//! 并展示了它们之间的兼容性，包括日志文件输出功能和配置文件支持

use serde::{Deserialize, Serialize};
use std::fs;
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::prelude::*;
use tracing_subscriber::Registry;

/// 日志格式配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FormatConfig {
    /// 是否显示时间戳
    pub with_time: bool,
    /// 是否显示目标（模块路径）
    pub with_target: bool,
    /// 是否显示线程信息
    pub with_thread_ids: bool,
    /// 是否显示线程名称
    pub with_thread_names: bool,
    /// 是否使用 ANSI 颜色
    pub ansi: bool,
}

impl Default for FormatConfig {
    fn default() -> Self {
        Self {
            with_time: true,
            with_target: true,
            with_thread_ids: false,
            with_thread_names: false,
            ansi: true,
        }
    }
}

/// 不同级别日志的特殊配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LevelBasedConfig {
    /// 日志级别
    pub level: String,
    /// 该级别的日志是否输出到文件
    pub file_output: Option<String>,
    /// 该级别的日志格式配置
    pub format: Option<FormatConfig>,
}

/// 日志配置结构体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogConfig {
    /// 默认日志级别
    pub level: String,
    /// 是否输出到控制台
    pub console_output: bool,
    /// 默认日志文件路径（可选）
    pub file_path: Option<String>,
    /// 默认格式配置
    #[serde(default)]
    pub format: FormatConfig,
    /// 不同级别日志的特殊配置
    #[serde(default)]
    pub level_based_configs: Vec<LevelBasedConfig>,
}

impl Default for LogConfig {
    fn default() -> Self {
        Self {
            level: "info".to_string(),
            console_output: true,
            file_path: None,
            format: FormatConfig::default(),
            level_based_configs: vec![],
        }
    }
}

impl LogConfig {
    /// 合并两个配置，后者覆盖前者
    fn merge(base: LogConfig, override_config: LogConfig) -> LogConfig {
        LogConfig {
            level: if override_config.level.is_empty() { base.level } else { override_config.level },
            console_output: override_config.console_output,
            file_path: override_config.file_path.or(base.file_path),
            format: FormatConfig {
                with_time: override_config.format.with_time,
                with_target: override_config.format.with_target,
                with_thread_ids: override_config.format.with_thread_ids,
                with_thread_names: override_config.format.with_thread_names,
                ansi: override_config.format.ansi,
            },
            level_based_configs: if override_config.level_based_configs.is_empty() { base.level_based_configs } else { override_config.level_based_configs },
        }
    }
}

/// 配置文件中的 profiles 部分
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ProfilesConfig {
    /// 激活的环境配置
    pub active: Option<String>,
}

/// 环境特定的配置
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EnvironmentConfig {
    /// profiles 配置
    #[serde(default)]
    pub profiles: ProfilesConfig,
    /// 公共配置（所有环境共享）
    #[serde(default)]
    pub common: Option<LogConfig>,
    /// 开发环境配置
    #[serde(default)]
    pub dev: Option<LogConfig>,
    /// 测试环境配置
    #[serde(default)]
    pub test: Option<LogConfig>,
    /// 生产环境配置
    #[serde(default)]
    pub prod: Option<LogConfig>,
}

/// 从 TOML 配置文件初始化日志系统
pub fn init_logger_from_config(config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
    // 尝试读取配置文件，如果不存在则使用默认配置
    let env_config = if let Ok(config_str) = fs::read_to_string(config_path) { toml::from_str::<EnvironmentConfig>(&config_str)? } else { EnvironmentConfig::default() };

    // 根据配置文件中的 profiles.active 选择配置，并与公共配置合并
    let base_config = env_config.common.unwrap_or_default();

    // 使用配置文件中的 profiles.active
    let active_profile = if let Some(active) = env_config.profiles.active { active } else { "dev".to_string() };

    let config = {
        let env_specific_config = match active_profile.as_str() {
            "dev" | "development" => env_config.dev,
            "test" => env_config.test,
            "prod" | "production" => env_config.prod,
            _ => None,
        };

        // 合并公共配置和环境特定配置
        if let Some(specific) = env_specific_config {
            LogConfig::merge(base_config, specific)
        } else {
            base_config
        }
    };

    init_logger(config)
}

/// 根据配置初始化日志系统
pub fn init_logger(config: LogConfig) -> Result<(), Box<dyn std::error::Error>> {
    let level = match config.level.to_lowercase().as_str() {
        "error" => LevelFilter::ERROR,
        "warn" => LevelFilter::WARN,
        "info" => LevelFilter::INFO,
        "debug" => LevelFilter::DEBUG,
        "trace" => LevelFilter::TRACE,
        _ => LevelFilter::INFO,
    };

    let mut layers = Vec::new();

    // 配置控制台输出
    if config.console_output {
        let console_layer = create_console_layer(&config.format);
        layers.push(console_layer.with_filter(level).boxed());
    }

    // 配置默认文件输出
    if let Some(file_path) = &config.file_path {
        // 确保日志目录存在
        if let Some(parent) = std::path::Path::new(file_path).parent() {
            let _ = fs::create_dir_all(parent);
        }

        let file = std::fs::File::create(file_path)?;
        let file_layer = tracing_subscriber::fmt::layer()
            .with_writer(file)
            .with_ansi(false); // 文件通常不使用 ANSI 颜色
        layers.push(file_layer.with_filter(level).boxed());
    }

    // 配置基于级别的特殊输出
    for level_config in &config.level_based_configs {
        if let Some(file_path) = &level_config.file_output {
            // 确保日志目录存在
            if let Some(parent) = std::path::Path::new(file_path).parent() {
                let _ = fs::create_dir_all(parent);
            }

            let file = std::fs::File::create(file_path)?;

            let level_filter = match level_config.level.to_lowercase().as_str() {
                "error" => LevelFilter::ERROR,
                "warn" => LevelFilter::WARN,
                "info" => LevelFilter::INFO,
                "debug" => LevelFilter::DEBUG,
                "trace" => LevelFilter::TRACE,
                _ => LevelFilter::INFO,
            };

            let file_layer = if let Some(format_config) = &level_config.format {
                create_file_layer(format_config, file)
            } else {
                Box::new(
                    tracing_subscriber::fmt::layer()
                        .with_writer(file)
                        .with_ansi(false),
                )
            };

            layers.push(file_layer.with_filter(level_filter).boxed());
        }
    }

    let subscriber = tracing_subscriber::registry().with(layers);

    // 只有在尚未设置全局默认值时才设置
    let _ = tracing::subscriber::set_global_default(subscriber);

    Ok(())
}

/// 创建控制台输出层
fn create_console_layer(format_config: &FormatConfig) -> Box<dyn tracing_subscriber::Layer<Registry> + Send + Sync> {
    let layer = tracing_subscriber::fmt::layer()
        .with_ansi(format_config.ansi)
        .with_target(format_config.with_target)
        .with_thread_ids(format_config.with_thread_ids)
        .with_thread_names(format_config.with_thread_names);

    if format_config.with_time {
        Box::new(layer.with_timer(tracing_subscriber::fmt::time::SystemTime))
    } else {
        Box::new(layer.without_time())
    }
}

/// 创建文件输出层
fn create_file_layer(format_config: &FormatConfig, writer: std::fs::File) -> Box<dyn tracing_subscriber::Layer<Registry> + Send + Sync> {
    let layer = tracing_subscriber::fmt::layer()
        .with_writer(writer)
        .with_ansi(false) // 文件通常不使用 ANSI 颜色
        .with_target(format_config.with_target)
        .with_thread_ids(format_config.with_thread_ids)
        .with_thread_names(format_config.with_thread_names);

    if format_config.with_time {
        Box::new(layer.with_timer(tracing_subscriber::fmt::time::SystemTime))
    } else {
        Box::new(layer.without_time())
    }
}

/// 生成默认的 TOML 配置文件示例
pub fn generate_default_config() -> String {
    r#"# 日志配置文件
# 该文件由 log_demo 程序自动生成
# 可以修改此文件来配置日志行为
# 通过设置 profiles.active 来选择使用哪种环境配置
# 例如: active = "dev" 或 active = "test" 或 active = "prod"

# profiles 配置
[profiles]
# 激活的环境配置: dev, test, prod
# dev: 开发环境 - 启用详细日志，输出到控制台和文件，便于调试
# test: 测试环境 - 输出详细日志到文件，便于测试结果分析
# prod: 生产环境 - 只输出重要日志，优化性能
active = "dev"

# 公共配置（所有环境共享）
[common]
# 默认日志级别: trace, debug, info, warn, error
# 生产环境建议使用 warn 或 error 级别以减少日志量
# 开发和测试环境可以使用 info 或 debug 级别以便调试
level = "info"

# 是否输出到控制台
# 开发环境建议启用，便于实时查看日志
# 生产环境可以禁用以提高性能
console_output = true

# 公共格式配置
[common.format]
# 是否显示时间戳
# 所有环境建议启用，便于问题追踪
with_time = true

# 是否显示目标（模块路径）
# 所有环境建议启用，便于定位日志来源
with_target = true

# 是否显示线程ID
# 开发和测试环境建议启用，便于调试多线程问题
# 生产环境可以禁用以提高性能
with_thread_ids = false

# 是否显示线程名称
# 开发和测试环境建议启用，便于调试多线程问题
# 生产环境可以禁用以提高性能
with_thread_names = false

# 是否使用 ANSI 颜色
# 开发环境建议启用，便于区分不同级别的日志
# 测试和生产环境建议禁用，避免颜色代码干扰日志分析
ansi = true

# 开发环境配置
# 适用于开发阶段，提供详细的日志信息便于调试
[dev]
# 开发环境使用 debug 级别，输出详细信息
level = "debug"

# 开发环境日志文件路径
file_path = "logs/dev.log"

# 开发环境格式配置
[dev.format]
# 开发环境启用 ANSI 颜色以便于查看
ansi = true

# 测试环境配置
# 适用于自动化测试阶段，输出结构化日志便于分析
[test]
# 测试环境禁用控制台输出，避免干扰测试结果
console_output = false

# 测试环境日志文件路径
file_path = "logs/test.log"

# 测试环境格式配置
[test.format]
# 测试环境启用线程信息，便于分析多线程测试
with_thread_ids = true
with_thread_names = true

# 测试环境禁用 ANSI 颜色，避免颜色代码干扰日志分析
ansi = false

# 生产环境配置
# 适用于生产部署，优化性能，只记录重要信息
[prod]
# 生产环境使用 warn 级别，只记录警告和错误
level = "warn"

# 生产环境日志文件路径
file_path = "logs/prod.log"

# 生产环境格式配置
[prod.format]
# 生产环境禁用 ANSI 颜色，避免颜色代码干扰日志分析
ansi = false

# 不同级别日志的特殊配置（这里定义在公共配置中，所有环境都会继承）
# 错误级别日志单独输出到错误日志文件，便于问题追踪
[[common.level_based_configs]]
level = "error"
file_output = "logs/error.log"

# 错误日志格式配置
[common.level_based_configs.format]
# 错误日志包含详细信息便于问题分析
with_time = true
with_target = true
with_thread_ids = true
with_thread_names = true
ansi = false

# 警告级别日志单独输出到警告日志文件
[[common.level_based_configs]]
level = "warn"
file_output = "logs/warn.log"
"#
    .to_string()
}

/// 将默认配置写入文件
pub fn write_default_config_file(path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let config_content = generate_default_config();
    fs::create_dir_all(
        std::path::Path::new(path)
            .parent()
            .unwrap_or_else(|| std::path::Path::new(".")),
    )?;
    fs::write(path, config_content)?;
    Ok(())
}

/// 测试 log 和 tracing 集成的基本功能
#[test]
fn test_log_tracing_integration() {
    use log::{error as log_error, info as log_info, warn as log_warn};
    use tracing::{error as tracing_error, info as tracing_info, warn as tracing_warn};

    // 使用默认配置初始化日志系统
    let config = LogConfig::default();
    let _ = init_logger(config); // 忽略可能的错误

    // 使用 log crate 记录日志
    log_info!("这是一条通过 log crate 记录的信息日志");
    log_warn!("这是一条通过 log crate 记录的警告日志");
    log_error!("这是一条通过 log crate 记录的错误日志");

    // 使用 tracing crate 记录日志
    tracing_info!("这是一条通过 tracing crate 记录的信息日志");
    tracing_warn!("这是一条通过 tracing crate 记录的警告日志");
    tracing_error!("这是一条通过 tracing crate 记录的错误日志");

    // 演示结构化日志记录
    let user_id = 123;
    let username = "Alice";
    tracing_info!(user_id, username, "用户登录");

    // 演示 span 的使用
    let span = tracing::info_span!("处理用户请求", user_id, username);
    let _guard = span.enter();

    tracing_info!("开始处理用户请求");

    // 模拟一些处理逻辑
    if let Some(value) = Some(42) {
        tracing_info!(calculated_value = value, "计算完成");
    }

    tracing_info!("用户请求处理完成");
}

/// 演示日志级别控制
#[test]
fn test_log_levels() {
    use tracing::{debug, error, info, trace, warn};

    // 设置日志级别为 WARN
    let config = LogConfig { level: "warn".to_string(), ..Default::default() };

    let _ = init_logger(config); // 忽略可能的错误

    // 这些信息不会被输出，因为级别低于 WARN
    trace!("这条跟踪信息不会显示");
    debug!("这条调试信息也不会显示");
    info!("这条信息也不会显示");

    // 这些信息会被输出
    warn!("这是一条警告信息");
    error!("这是一条错误信息");
}

/// 演示 log 和 tracing 的过滤器功能
#[test]
fn test_filters() {
    use tracing::info;

    // 使用默认配置
    let config = LogConfig::default();
    let _ = init_logger(config); // 忽略可能的错误
    info!("使用默认日志配置");
}

/// 演示日志文件输出功能
#[test]
fn test_file_output() {
    use log::{error as log_error, info as log_info, warn as log_warn};
    use std::path::Path;
    use tracing::{error as tracing_error, info as tracing_info, warn as tracing_warn};

    // 配置日志输出到文件
    let config = LogConfig {
        level: "info".to_string(),
        console_output: false, // 只输出到文件，不输出到控制台
        file_path: Some("logs/test_file_output.log".to_string()),
        ..Default::default()
    };

    let _ = init_logger(config); // 忽略可能的错误

    // 使用 log crate 记录日志
    log_info!("这是一条通过 log crate 记录的信息日志（写入文件）");
    log_warn!("这是一条通过 log crate 记录的警告日志（写入文件）");
    log_error!("这是一条通过 log crate 记录的错误日志（写入文件）");

    // 使用 tracing crate 记录日志
    tracing_info!("这是一条通过 tracing crate 记录的信息日志（写入文件）");
    tracing_warn!("这是一条通过 tracing crate 记录的警告日志（写入文件）");
    tracing_error!("这是一条通过 tracing crate 记录的错误日志（写入文件）");

    // 演示结构化日志记录
    let user_id = 456;
    let username = "Bob";
    tracing_info!(user_id, username, "用户登录（写入文件）");

    // 演示 span 的使用
    let span = tracing::info_span!("处理用户请求", user_id, username);
    let _guard = span.enter();

    tracing_info!("开始处理用户请求（写入文件）");

    // 模拟一些处理逻辑
    if let Some(value) = Some(100) {
        tracing_info!(calculated_value = value, "计算完成（写入文件）");
    }

    tracing_info!("用户请求处理完成（写入文件）");

    // 验证日志文件是否创建
    assert!(Path::new("logs/test_file_output.log").exists());
}

/// 演示不同日志级别的文件输出
#[test]
fn test_file_output_with_levels() {
    use std::path::Path;
    use tracing::{error, info, warn};

    // 配置日志输出到文件
    let config = LogConfig {
        level: "info".to_string(),
        console_output: false, // 只输出到文件，不输出到控制台
        file_path: Some("logs/test_levels.log".to_string()),
        ..Default::default()
    };

    let _ = init_logger(config); // 忽略可能的错误

    // 记录不同级别的日志
    info!("这是一条信息日志");
    warn!("这是一条警告日志");
    error!("这是一条错误日志");

    // 结构化日志
    let user_id = 789;
    info!(user_id, "用户相关信息");
    error!(user_id, "用户相关错误");

    // 验证日志文件是否创建
    assert!(Path::new("logs/test_levels.log").exists());
}

/// 演示从 TOML 配置文件加载日志配置
#[test]
fn test_config_file_loading() {
    use std::fs;
    use tracing::info;

    // 创建 TOML 配置文件
    let config_content = r#"# 测试配置文件
[default]
level = "debug"
console_output = true
file_path = "logs/config_test.log"

[default.format]
with_time = true
with_target = true
with_thread_ids = false
with_thread_names = false
ansi = false
"#;

    // 写入配置文件
    fs::create_dir_all("logs").expect("创建日志目录失败");
    fs::write("logs/test_config.toml", config_content).expect("写入配置文件失败");

    // 从配置文件加载并初始化日志系统
    let _ = init_logger_from_config("logs/test_config.toml"); // 忽略可能的错误

    info!("使用 TOML 配置文件初始化的日志系统");
    info!("这条调试信息应该会显示");
}

/// 演示生成默认配置文件
#[test]
fn test_generate_default_config() {
    let default_config = generate_default_config();
    assert!(default_config.contains("# 日志配置文件"));
    assert!(default_config.contains("level = \"info\""));
    assert!(default_config.contains("file_path = \"logs/error.log\"") || default_config.contains("[[common.level_based_configs]]"));
}

/// 演示写入默认配置文件
#[test]
fn test_write_default_config_file() {
    use std::path::Path;

    let config_path = "logs/default_config.toml";
    let result = write_default_config_file(config_path);
    assert!(result.is_ok());
    assert!(Path::new(config_path).exists());
}

/// 演示不同级别日志的不同格式配置
#[test]
fn test_level_based_format_config() {
    use std::path::Path;
    use tracing::{error, info, warn};

    // 配置不同级别日志的不同输出和格式
    let config = LogConfig {
        level: "trace".to_string(),
        console_output: false, // 不输出到控制台
        file_path: Some("logs/default.log".to_string()),
        format: FormatConfig {
            with_time: true,
            with_target: true,
            with_thread_ids: false,
            with_thread_names: false,
            ansi: false,
        },
        level_based_configs: vec![
            LevelBasedConfig {
                level: "error".to_string(),
                file_output: Some("logs/error.log".to_string()),
                format: Some(FormatConfig {
                    with_time: true,
                    with_target: true,
                    with_thread_ids: true,
                    with_thread_names: true,
                    ansi: false,
                }),
            },
            LevelBasedConfig {
                level: "warn".to_string(),
                file_output: Some("logs/warn.log".to_string()),
                format: Some(FormatConfig {
                    with_time: true,
                    with_target: false,
                    with_thread_ids: false,
                    with_thread_names: false,
                    ansi: false,
                }),
            },
        ],
    };

    let _ = init_logger(config); // 忽略可能的错误

    // 记录不同级别的日志
    info!("这是一条信息日志");
    warn!("这是一条警告日志");
    error!("这是一条错误日志");

    // 验证日志文件是否创建
    assert!(Path::new("logs/default.log").exists());
    assert!(Path::new("logs/error.log").exists());
    assert!(Path::new("logs/warn.log").exists());
}

/// 演示环境配置功能
#[test]
fn test_environment_config() {
    use std::fs;
    use std::path::Path;
    use tracing::info;

    // 创建包含环境配置的 TOML 文件
    let config_content = r#"# 环境配置测试文件
[profiles]
active = "test"

[common]
level = "info"
console_output = true

[common.format]
with_time = true
with_target = true
with_thread_ids = false
with_thread_names = false
ansi = true

[test]
level = "debug"
console_output = false
file_path = "logs/env_test.log"

[test.format]
with_time = true
with_target = true
with_thread_ids = true
with_thread_names = true
ansi = false
"#;

    // 写入配置文件
    fs::create_dir_all("logs").expect("创建日志目录失败");
    fs::write("logs/env_config_test.toml", config_content).expect("写入配置文件失败");

    // 使用配置文件初始化日志系统
    let _ = init_logger_from_config("logs/env_config_test.toml");

    info!("使用环境配置初始化的日志系统");

    // 验证日志文件是否创建
    assert!(Path::new("logs/env_test.log").exists(), "测试环境的日志文件应该被创建");
}
